// Copyright 2024 Vadim Kharitonovich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package adapter

import (
	"context"
	"fmt"
	"net"
	"time"

	"github.com/rs/zerolog"
	"notabug.org/overflowingd/lbs/pkg/log"
)

type eventFactory func() *zerolog.Event

type zl struct {
	l      *zerolog.Logger
	levels []eventFactory
}

func NewZerolog(l zerolog.Logger) *zl {
	return &zl{
		l: &l,
		levels: []eventFactory{
			log.DebugLevel: l.Debug,
			log.InfoLevel:  l.Info,
			log.WarnLevel:  l.Warn,
			log.ErrorLevel: l.Error,
			log.FatalLevel: l.Fatal,
		},
	}
}

func (r *zl) Debug(msg string, opts ...log.Opt) {
	r.Log(log.DebugLevel, msg, opts...)
}

func (r *zl) Info(msg string, opts ...log.Opt) {
	r.Log(log.InfoLevel, msg, opts...)
}

func (r *zl) Warn(msg string, opts ...log.Opt) {
	r.Log(log.WarnLevel, msg, opts...)
}

func (r *zl) Error(msg string, opts ...log.Opt) {
	r.Log(log.ErrorLevel, msg, opts...)
}

func (r *zl) Fatal(msg string, opts ...log.Opt) {
	r.Log(log.FatalLevel, msg, opts...)
}

func (r *zl) Enabled(level log.Level) bool {
	return r.levels[level]().Enabled()
}

func (r *zl) Func(level log.Level, f func()) {
	if r.Enabled(level) {
		f()
	}
}

func (r *zl) Log(level log.Level, msg string, opts ...log.Opt) {
	var builder = zerologBuilder{
		Event: r.levels[level](),
	}

	for i := range opts {
		opts[i](&builder)
	}

	if builder.format != nil {
		builder.Msgf(msg, builder.format...)
		return
	}

	builder.Msg(msg)
}

type zerologBuilder struct {
	*zerolog.Event

	format []any
}

func (r *zerologBuilder) Context(ctx context.Context) {
	r.Event.Ctx(ctx)
}

func (r *zerologBuilder) Format(args ...any) {
	r.format = args
}

func (r *zerologBuilder) Str(key, val string) {
	r.Event.Str(key, val)
}

func (r *zerologBuilder) Strs(key string, vals []string) {
	r.Event.Strs(key, vals)
}

func (r *zerologBuilder) Stringer(key string, val fmt.Stringer) {
	r.Event.Stringer(key, val)
}

func (r *zerologBuilder) Stringers(key string, vals []fmt.Stringer) {
	r.Event.Stringers(key, vals)
}

func (r *zerologBuilder) Bytes(key string, val []byte) {
	r.Event.Bytes(key, val)
}

func (r *zerologBuilder) Hex(key string, val []byte) {
	r.Event.Hex(key, val)
}

func (r *zerologBuilder) RawJSON(key string, b []byte) {
	r.Event.RawJSON(key, b)
}

func (r *zerologBuilder) RawCBOR(key string, b []byte) {
	r.Event.RawCBOR(key, b)
}

func (r *zerologBuilder) AnErr(key string, err error) {
	r.Event.AnErr(key, err)
}

func (r *zerologBuilder) Errs(key string, errs []error) {
	r.Event.Errs(key, errs)
}

func (r *zerologBuilder) Err(err error) {
	r.Event.Err(err)
}

func (r *zerologBuilder) Stack() {
	r.Event.Stack()
}

func (r *zerologBuilder) Bool(key string, b bool) {
	r.Event.Bool(key, b)
}

func (r *zerologBuilder) Bools(key string, b []bool) {
	r.Event.Bools(key, b)
}

func (r *zerologBuilder) Int(key string, i int) {
	r.Event.Int(key, i)
}

func (r *zerologBuilder) Ints(key string, i []int) {
	r.Event.Ints(key, i)
}

func (r *zerologBuilder) Int8(key string, i int8) {
	r.Event.Int8(key, i)
}

func (r *zerologBuilder) Ints8(key string, i []int8) {
	r.Event.Ints8(key, i)
}

func (r *zerologBuilder) Int16(key string, i int16) {
	r.Event.Int16(key, i)
}

func (r *zerologBuilder) Ints16(key string, i []int16) {
	r.Event.Ints16(key, i)
}

func (r *zerologBuilder) Int32(key string, i int32) {
	r.Event.Int32(key, i)
}

func (r *zerologBuilder) Ints32(key string, i []int32) {
	r.Event.Ints32(key, i)
}

func (r *zerologBuilder) Int64(key string, i int64) {
	r.Event.Int64(key, i)
}

func (r *zerologBuilder) Ints64(key string, i []int64) {
	r.Event.Ints64(key, i)
}

func (r *zerologBuilder) Uint(key string, i uint) {
	r.Event.Uint(key, i)
}

func (r *zerologBuilder) Uints(key string, i []uint) {
	r.Event.Uints(key, i)
}

func (r *zerologBuilder) Uint8(key string, i uint8) {
	r.Event.Uint8(key, i)
}

func (r *zerologBuilder) Uints8(key string, i []uint8) {
	r.Event.Uints8(key, i)
}

func (r *zerologBuilder) Uint16(key string, i uint16) {
	r.Event.Uint16(key, i)
}

func (r *zerologBuilder) Uints16(key string, i []uint16) {
	r.Event.Uints16(key, i)
}

func (r *zerologBuilder) Uint32(key string, i uint32) {
	r.Event.Uint32(key, i)
}

func (r *zerologBuilder) Uints32(key string, i []uint32) {
	r.Event.Uints32(key, i)
}

func (r *zerologBuilder) Uint64(key string, i uint64) {
	r.Event.Uint64(key, i)
}

func (r *zerologBuilder) Uints64(key string, i []uint64) {
	r.Event.Uints64(key, i)
}

func (r *zerologBuilder) Float32(key string, f float32) {
	r.Event.Float32(key, f)
}

func (r *zerologBuilder) Floats32(key string, f []float32) {
	r.Event.Floats32(key, f)
}

func (r *zerologBuilder) Float64(key string, f float64) {
	r.Event.Float64(key, f)
}

func (r *zerologBuilder) Floats64(key string, f []float64) {
	r.Event.Floats64(key, f)
}

func (r *zerologBuilder) Timestamp() {
	r.Event.Timestamp()
}

func (r *zerologBuilder) Times(key string, t []time.Time) {
	r.Event.Times(key, t)
}

func (r *zerologBuilder) Dur(key string, d time.Duration) {
	r.Event.Dur(key, d)
}

func (r *zerologBuilder) Durs(key string, d []time.Duration) {
	r.Event.Durs(key, d)
}

func (r *zerologBuilder) TimeDiff(key string, t time.Time, start time.Time) {
	r.Event.TimeDiff(key, t, start)
}

func (r *zerologBuilder) Any(key string, i interface{}) {
	r.Event.Any(key, i)
}

func (r *zerologBuilder) Interface(key string, i interface{}) {
	r.Event.Interface(key, i)
}

func (r *zerologBuilder) Type(key string, val interface{}) {
	r.Event.Type(key, val)
}

func (r *zerologBuilder) IPAddr(key string, ip net.IP) {
	r.Event.IPAddr(key, ip)
}

func (r *zerologBuilder) IPPrefix(key string, pfx net.IPNet) {
	r.Event.IPPrefix(key, pfx)
}

func (r *zerologBuilder) MACAddr(key string, ha net.HardwareAddr) {
	r.Event.MACAddr(key, ha)
}
